package com.bensoft.minesweeper.gui.minesweeperframe.grid;

import com.bensoft.minesweeper.ApplicationController;
import static com.bensoft.minesweeper.GameManager.GameState.READY;
import static com.bensoft.minesweeper.GameManager.GameState.RUNNING;
import com.bensoft.minesweeper.gui.GUILoader;
import com.bensoft.minesweeper.gui.minesweeperframe.DisplayPanel;
import com.bensoft.minesweeper.gui.preferences.ApplicationPreferences;
import com.bensoft.minesweeper.resources.FileResources;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.BevelBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.MouseInputAdapter;

/**
 *
 * @author Ben Wolsieffer
 */
public class GridSpace extends JPanel implements GUILoader {

    private State state = State.NOT_CLICKED;

    static {
        FileResources.addThemeChangeListener(new FileResources.ThemeChangeListener() {
            @Override
            public void themeChanged() {
                // Change colors
                color = FileResources.getColor("gridspace.color");
                highlightOuterColor = FileResources.getColor("gridspace.border.outerhighlight");
                highlightInnerColor = FileResources.getColor("gridspace.border.innerhighlight");
                shadowOuterColor = FileResources.getColor("gridspace.border.outershadow");
                shadowInnerColor = FileResources.getColor("gridspace.border.innershadow");

                raisedBorder = new BevelBorder(BevelBorder.RAISED, highlightOuterColor, highlightInnerColor, shadowOuterColor, shadowInnerColor);
            }
        });
    }

    public static enum State {

        /**
         * Returned by
         * <code>getState()</code> if the grid space has been left clicked.
         */
        LEFT_CLICKED,
        /**
         * Returned by
         * <code>getState()</code> if the grid space is showing a flag.
         */
        FLAG,
        /**
         * Returned by
         * <code>getState()</code> if the grid space is showing a question mark.
         */
        QUESTION,
        /**
         * Returned by
         * <code>getState()</code> if the grid space has not been clicked, or
         * returned to its original state.
         */
        NOT_CLICKED,
        /**
         * Returned by
         * <code>getState()</code> if the grid space was marked as a bomb but
         * really isn't.
         */
        MINE_X;
    }
    private static Color color;
    private static Color highlightOuterColor;
    private static Color highlightInnerColor;
    private static Color shadowOuterColor;
    private static Color shadowInnerColor;
    public final static int SIZE = 16;
    private int x;
    private int y;
    private boolean mine = false;
    private static LineBorder lineBorder = new LineBorder(Color.gray, 1);
    @SuppressWarnings("StaticNonFinalUsedInInitialization")
    private static BevelBorder raisedBorder;
    private Image icon;
    private int number = 0;

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public int getGridX() {
        return x;
    }

    public int getGridY() {
        return y;
    }

    public GridSpace(int x, int y, boolean mine) {
        this.x = x;
        this.y = y;
        this.mine = mine;
        if (mine) {
            number = -1;
        }
        init();
    }

    /**
     * Gets the state of the grid space. Returns one of four values:
     * <code>STATE_NOT_CLICKED</code>,
     * <code>STATE_LEFT_CLICKED</code>,
     * <code>STATE_FLAG</code> or
     * <code>STATE_QUESTION</code>.
     *
     * @return The state of the grid space.
     */
    public State getState() {
        return state;
    }

    public void setState(State state) {
        if (state != this.state && (state == State.QUESTION ? !ApplicationPreferences.areQuestionMarksEnabled() : true)) {
            this.state = state;
            switch (this.state) {
                case NOT_CLICKED:
                    icon = null;
                    setBorder(raisedBorder);
                    break;
                case FLAG:
                    icon = FileResources.getFlagImage();
                    setBorder(raisedBorder);
                    break;
                case LEFT_CLICKED:
                    icon = FileResources.getMineImage();
                    setBorder(lineBorder);
                    break;
                case QUESTION:
                    icon = FileResources.getQuestionImage();
                    setBorder(raisedBorder);
                case MINE_X:
                    icon = FileResources.getMineXImage();
                    setBorder(lineBorder);
            }
        }

    }

    public GridSpace(int x, int y) {
        this(x, y, false);
    }

    private void init() {
        initializeComponents();
        addComponents();
        addComponentListeners();
        otherInit();
    }

    @Override
    public void initializeComponents() {
    }

    @Override
    public void setLayouts() {
    }

    @Override
    public void addComponents() {
    }

    @Override
    public void addComponentListeners() {
        addMouseListener(new MouseInputAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                switch (ApplicationController.getGameManager().getGameState()) {
                    case READY:
                    case RUNNING:
                        // Mouse released is always called over the GridSpace that the 
                        // press started so I need to calculate if the mouse moved off
                        // of the space while the mouse button was pressed and call its 
                        // leftClick() method accordingly.
                        Rectangle bounds = getBounds();
                        // Convert the point to the parent coordinate system
                        Point p = SwingUtilities.convertPoint(getThis(), e.getPoint(), getParent());
                        GridSpace c = null;
                        // Check if the point is with in the bounds of this space 
                        // (ie, whether or not the mouse moved off this space)
                        if (bounds.contains(p)) {
                            // If it is, click on this space
                            c = getThis();
                        } else {
                            // If it is not, get the component that the mouse is over
                            Component cc = getParent().getComponentAt(p);
                            // And check to see if it is a GridSpace
                            if (cc instanceof GridSpace) {
                                // If it is, click on it
                                c = (GridSpace) cc;
                            }
                        }
                        // If c is not null (ie the click was released within the grid)
                        if (c != null) {
                            // and the left mouse button was released
                            if (SwingUtilities.isLeftMouseButton(e)) {
                                // click on the space
                                c.leftClick();
                                // and process the click.
                                //
                                // DO NOT MOVE to leftClick() because leftClick() gets
                                // called by processClick() and doing so that leads to
                                // infinite recursion
                                ApplicationController.getMinesweeperFrame().getGrid().processClick(c);
                            }
                        }
                        // Always reset the smiley button :)
                        if (!mine) {
                            ApplicationController.getMinesweeperFrame().getDisplayPanel().gridSpaceReleased();
                        }
                }
            }

            @Override
            public void mouseEntered(MouseEvent e) {
                switch (ApplicationController.getGameManager().getGameState()) {
                    case READY:
                    case RUNNING:
                        if (SwingUtilities.isLeftMouseButton(e) && state != State.FLAG) {
                            setBorder(lineBorder);
                        }
                        ApplicationController.setCheatWhite(!mine);
                }
            }

            @Override
            public void mouseExited(MouseEvent e) {
                switch (ApplicationController.getGameManager().getGameState()) {
                    case READY:
                    case RUNNING:
                        if (state == State.NOT_CLICKED) {
                            setBorder(raisedBorder);
                        }
                        ApplicationController.setCheatWhite(true);
                }
            }

            @Override
            public void mousePressed(MouseEvent e) {
                switch (ApplicationController.getGameManager().getGameState()) {
                    case READY:
                    case RUNNING:
                        mouseEntered(e);
                        if (SwingUtilities.isLeftMouseButton(e) && state != State.FLAG) {
                            ApplicationController.getMinesweeperFrame().getDisplayPanel().gridSpacePressed();
                            setBorder(lineBorder);
                        } else if (SwingUtilities.isRightMouseButton(e)) {
                            rightClick();
                        }
                }
            }
        });
    }

    private GridSpace getThis() {
        return this;
    }

    @Override
    public void otherInit() {
        setSize(new Dimension(SIZE, SIZE));
        setPreferredSize(new Dimension(SIZE, SIZE));
        FileResources.addThemeChangeListener(new FileResources.ThemeChangeListener() {
            @Override
            public void themeChanged() {
                // Change image
                switch (state) {
                    case LEFT_CLICKED:
                        if (mine) {
                            icon = FileResources.getMineImage();
                        } else {
                            if (number == 0) {
                            } else {
                                icon = FileResources.getNumberImage(number);
                            }
                        }
                        break;
                    case FLAG:
                        icon = FileResources.getFlagImage();
                        setBorder(raisedBorder);
                        break;
                    case QUESTION:
                        icon = FileResources.getQuestionImage();
                        setBorder(raisedBorder);
                        break;
                    case MINE_X:
                        icon = FileResources.getMineXImage();
                        break;
                    case NOT_CLICKED:
                        setBorder(raisedBorder);
                }
                repaint();
            }
        });
    }

    public boolean isMine() {
        return mine;
    }

    public void leftClick() {
        switch (ApplicationController.getGameManager().getGameState()) {
            case READY:
                ApplicationController.getGameManager().startGame();
            case RUNNING:
                if (state == State.NOT_CLICKED || state == State.QUESTION) {
                    setBorder(lineBorder);
                    if (mine) {
                        icon = FileResources.getMineImage();
                    } else {
                        if (number == 0) {
                            icon = null;
                        } else {
                            icon = FileResources.getNumberImage(number);
                        }
                    }
                    state = State.LEFT_CLICKED;
                }
        }

        repaint();
    }

    private void rightClick() {
        DisplayPanel dp = ApplicationController.getMinesweeperFrame().getDisplayPanel();
        switch (state) {
            case FLAG:
                if (ApplicationPreferences.areQuestionMarksEnabled()) {
                    state = State.QUESTION;
                    icon = FileResources.getQuestionImage();
                } else {
                    state = State.NOT_CLICKED;
                    icon = null;
                }
                dp.setRemainingMines(dp.getRemainingMines() + 1);
                break;
            case QUESTION:
                state = State.NOT_CLICKED;
                icon = null;
                break;
            case NOT_CLICKED:
                state = State.FLAG;
                dp.setRemainingMines(dp.getRemainingMines() - 1);
                icon = FileResources.getFlagImage();
        }
        repaint();
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(color);
        g.fillRect(0, 0, SIZE, SIZE);
        g.drawImage(icon, 0, 0, this);
    }
}
